﻿/******************************************************************************
 * 创建者：gtts
 * 创建日期：2011.4.7
 * 类名：CacheList
 * 说明：本地缓存类(二叉树实现)
 ******************************************************************************/
using System;
using System.Collections.Generic;
using System.Text;

namespace GTTSOfLibrary.Common
{
    /// <summary>
    /// 本地缓存类
    /// </summary>
    /// <typeparam name="T">要缓存的类型</typeparam>
    /// <remarks>
    /// 这是一个固定长度的双向链表，没有闭合，当然是起初没写成闭合的。
    /// 怎么缓存的呢？ 就是比如 长度是3000，你缓存一篇文章 就放进去一个， 
    /// 如果你从里面获取一个A，则A被提到链表的头部，再放入一个B，则B在头部， 其他的就会靠后排。
    /// 如果缓存的数量超过的3000，再缓存新的时候会踢掉尾部的那个。因为那个用的次数很少。
    /// </remarks>
    /// <example>
    /// CacheList使用方法:
    /// <code>
    /// public class ArticleCacheProvider
    /// {
    ///    private static readonly CacheList&lt;Article&gt; CacheList;
    ///    private static string GetCacheKey(string fileNameOrArticleId, int blogId)
    ///    {
    ///        return ("article_" + fileNameOrArticleId + "_" + blogId).ToLower();
    ///    }
    ///    static ArticleCacheProvider()
    ///    {
    ///        CacheList = new CacheList&lt;Article&gt;(3000);
    ///    }
    ///    public static Article Get(string fileNameOrArticleId, int blogId)
    ///    {
    ///        return CacheList.Get(GetCacheKey(fileNameOrArticleId, blogId));
    ///    }
    /// public static void Set(Article entity)
    /// {
    ///     //如果文章有别名，则存2份key，filename和articleId都可以获取缓存实体
    ///     if (entity.FileName != entity.ArticleId.ToString())
    ///     {
    ///         CacheList.Add(GetCacheKey(entity.ArticleId.ToString(), entity.BlogId), entity);
    ///     }
    ///     CacheList.Add(GetCacheKey(entity.FileName, entity.BlogId), entity);
    /// }
    /// public static void Remove(string fileNameOrArticleId, int blogId)
    /// {
    ///     CacheList.Remove(GetCacheKey(fileNameOrArticleId, blogId));
    ///     }
    ///     public static bool HasArticle(string fileNameOrArticleId, int blogId)
    /// {
    ///     return CacheList.HasKey(GetCacheKey(fileNameOrArticleId, blogId));
    /// }
    /// }
    /// </code>
    /// </example>
    public class CacheList<T> where T : class
    {
        /// <summary>
        /// 缓存实体
        /// </summary>
        public class Item
        {
            /// <summary>
            /// 获取或设置 键
            /// </summary>
            public string Key { get; set; }

            /// <summary>
            /// 获取或设置 值
            /// </summary>
            public T Value { get; set; }

            /// <summary>
            /// 获取或设置一个值，该值为左孩子
            /// </summary>
            public Item Left { get; set; }

            /// <summary>
            /// 获取或设置一个值，该值为右孩子
            /// </summary>
            public Item Right { get; set; }

            /// <summary>
            /// 构造函数
            /// </summary>
            /// <param name="key">键</param>
            /// <param name="value">值</param>
            public Item(string key, T value)
            {
                this.Key = key;
                this.Value = value;
            }
        }

        private readonly static object locked = new object();
        private readonly IDictionary<string, Item> dictionary;
        /// <summary>
        /// 获取缓存最大长度
        /// </summary>
        public int Length { get; private set; }

        /// <summary>
        /// 构造函数（初始化缓存最大长度）
        /// </summary>
        /// <param name="maxLength">缓存最大长度</param>
        public CacheList(int maxLength)
        {
            dictionary = new Dictionary<string, Item>();
            this.Length = maxLength;
        }

        private Item first;
        private Item last;

        /// <summary>
        /// 缓存中是否有某个键，即判断是否有需要的缓存
        /// </summary>
        /// <param name="key">要判断的键值</param>
        /// <returns>布尔型</returns>
        public bool HasKey(string key)
        {
            lock (locked)
            {
                return dictionary.ContainsKey(key);
            }
        }

        /// <summary>
        /// 添加缓存
        /// </summary>
        /// <param name="key">要添加的缓存的键</param>
        /// <param name="value">要添加缓存的值</param>
        public void Add(string key, T value)
        {
            var item = new Item(key, value);
            lock (locked)
            {
                if (dictionary.Count == 0)
                {
                    first = last = item;
                }
                else if (dictionary.Count == 1)
                {
                    last = first;
                    first = item;

                    last.Left = first;
                    first.Right = last;
                }
                else
                {
                    item.Right = first;
                    first.Left = item;
                    first = item;
                }
                if (dictionary.Count >= this.Length)
                {
                    last.Left.Right = null;
                    dictionary.Remove(last.Key);
                    last = last.Left;
                }
                dictionary.Add(key, item);
                // _dict.AddValue(key, item); //原来的
            }
        }
        /// <summary>
        /// 返回对应键的缓存数据
        /// </summary>
        /// <param name="key">要返回缓存的键</param>
        /// <returns>取到的缓存值</returns>
        public T Get(string key)
        {
            Item item = null;
            lock (locked)
            {
                dictionary.TryGetValue(key, out item);
            }
            if (item == null)
            {
                return null;
            }
            lock (locked)
            {
                if (dictionary.Count == 1)
                {
                    return first.Value;
                }
                if (item.Left != null)
                {
                    item.Left.Right = item.Right;
                }
                else
                {
                    return item.Value;
                }

                if (item.Right != null)
                {
                    item.Right.Left = item.Right;
                }
                else
                {
                    last.Left.Right = null;
                    last = last.Left;
                }
                item.Left = null;
                item.Right = first;
                first.Left = item;
                first = item;
            }
            return item.Value;
        }

        /// <summary>
        /// 从缓存中移除对应键的数据
        /// </summary>
        /// <param name="key">对应键</param>
        public void Remove(string key)
        {
            Item item = null;
            lock (locked)
            {
                dictionary.TryGetValue(key, out item);
            }
            if (item == null) return;
            lock (locked)
            {
                if (item.Left != null)
                {
                    item.Left.Right = item.Right;
                }
                else
                {
                    first = item.Right;
                }
                if (item.Right != null)
                {
                    item.Right.Left = item.Left;
                }
                else
                {
                    last = item.Left;
                }
                dictionary.Remove(key);
            }
        }
    }
}
